﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Research.Kinect.Nui;

namespace KinectMetroApp.KinectDetection
{
    public class Kinect
    {
        Runtime runtime = Runtime.Kinects[0];

        private event EventHandler NextItem;

        private event EventHandler PreviousItem;

        private BackgroundWorker selectItemBackWorker = null;


        public Kinect()
        {
            selectItemBackWorker = new BackgroundWorker();
            selectItemBackWorker.WorkerSupportsCancellation = true;
            selectItemBackWorker.DoWork += selectItemBackWorker_DoWork;
            selectItemBackWorker.RunWorkerCompleted += selectItemBackWorker_RunWorkerCompleted;

            NextItem += MainWindow_NextItem;
            PreviousItem += MainWindow_PreviousItem;

            runtime.SkeletonFrameReady += runtime_SkeletonFrameReady;

        }

        public void Start()
        {
            //Since only a color video stream is needed, RuntimeOptions.UseColor is used.
            runtime.Initialize(Microsoft.Research.Kinect.Nui.RuntimeOptions.UseColor | RuntimeOptions.UseSkeletalTracking);

            //You can adjust the resolution here.
            runtime.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);
        }

        private bool isItemShown = false;
        void selectItemBackWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
        {

            //if (!isItemShown)
            //{
            //    isItemShown = true;

            //    Client.Connect("ws://localhost:8052/server", (string)e.Result).Wait();

            //}
        }

        void selectItemBackWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            Client.Connect("ws://localhost:8052/server", (string)e.Argument).Wait();
        }

        private object oo = new object();
        void MainWindow_PreviousItem(object sender, EventArgs e)
        {
            lock (oo)
            {
                while (selectItemBackWorker.IsBusy)
                {
                    Thread.Sleep(100);
                }
                selectItemBackWorker.RunWorkerAsync("-");
            }
        }

        private object o = new object();
        void MainWindow_NextItem(object sender, EventArgs e)
        {
            lock (o)
            {
                while (selectItemBackWorker.IsBusy)
                {
                    Thread.Sleep(100);
                }
                selectItemBackWorker.RunWorkerAsync("+");
            }
        }


        void runtime_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
        {
            SkeletonFrame skeletonSet = e.SkeletonFrame;

            SkeletonData data = (from s in skeletonSet.Skeletons
                                 where s.TrackingState == SkeletonTrackingState.Tracked
                                 select s).FirstOrDefault();

            if (data != null)
            {
                SetEllipsePositionRight(data.Joints[JointID.HandRight], data.Joints[JointID.Head]);
                SetEllipsePositionLeft(data.Joints[JointID.HandLeft], data.Joints[JointID.Head]);
                GoToItem(data.Joints[JointID.HandRight], data.Joints[JointID.HandLeft], data.Joints[JointID.HipCenter]);
            }

        }

        private bool IsBetween(double input, double min, double max)
        {
            return ((input > max) && (input < min));
        }

        private bool hasSelectItem = false;
        private void GoToItem(Joint rightHand, Joint leftHand, Joint hipCenter)
        {
            float diff1 = hipCenter.Position.Z - rightHand.Position.Z;
            float diff2 = hipCenter.Position.Z - leftHand.Position.Z;
            //textBoxX.Text = "" + diff;
            if (diff1 + diff2 > 1)
            {
                if (!selectItemBackWorker.IsBusy && !hasSelectItem)
                {
                    while (selectItemBackWorker.IsBusy)
                    {
                        Thread.Sleep(100);
                    }

                    selectItemBackWorker.RunWorkerAsync("=");
                    hasSelectItem = true;
                }
            }
            else
            {
                hasSelectItem = false;
            }
            //if (IsBetween(leftHand.Position.Y, shoulderCenter.Position.Y, shoulderCenter.Position.Y + 2)
            //    && IsBetween(rightHand.Position.Y, shoulderCenter.Position.Y, shoulderCenter.Position.Y + 2)
            //    )
            //{
            //    if (!hasSelectItem && !selectItemBackWorker.CancellationPending)
            //    {
            //        hasSelectItem = true;
            //        selectItemBackWorker.RunWorkerAsync();
            //    }
            //}
            //else
            //{
            //    if (!selectItemBackWorker.CancellationPending)
            //    {
            //        selectItemBackWorker.CancelAsync();
            //    }
            //    hasSelectItem = false;
            //}
        }

        private bool isNextItem = false;
        private bool isRightUp = false;
        private void SetEllipsePositionRight(Joint jointRight, Joint jointHead)
        {
            Microsoft.Research.Kinect.Nui.Vector vector = new Microsoft.Research.Kinect.Nui.Vector();
            vector.Y = ScaleVector(600, -jointRight.Position.Y);
            vector.Z = jointRight.Position.Z;


            if ((jointRight.Position.Y < jointHead.Position.Y + 0.1) && (jointRight.Position.Y > jointHead.Position.Y - 0.1))
            {
                if (!isRightUp) { isRightUp = true; return; }
            }
            else if (jointRight.Position.Y > jointHead.Position.Y + 0.1)
            {
                if (!isNextItem)
                {
                    if (isRightUp)
                    {
                        isNextItem = true;
                        NextItem(null, null);
                        isRightUp = false;
                    }
                }
            }
            else
            {
                isNextItem = false;
            }


            Joint updatedJoint = new Joint();
            updatedJoint.ID = jointRight.ID;
            updatedJoint.TrackingState = JointTrackingState.Tracked;
            updatedJoint.Position = vector;
        }

        private bool isPreviousItem = false;
        private bool isLeftUp = false;
        private void SetEllipsePositionLeft( Joint jointLeft, Joint jointHead)
        {
            Microsoft.Research.Kinect.Nui.Vector vector = new Microsoft.Research.Kinect.Nui.Vector();
            //vector.X = ScaleVector(800, joint.Position.X);
            vector.Y = ScaleVector(600, -jointLeft.Position.Y);
            vector.Z = jointLeft.Position.Z;

            if ((jointLeft.Position.Y < jointHead.Position.Y + 0.1) && (jointLeft.Position.Y > jointHead.Position.Y - 0.1))
            {
                if (!isLeftUp) { isLeftUp = true; return; }
            }
            else if (jointLeft.Position.Y > jointHead.Position.Y + 0.1)
            {
                if (!isPreviousItem)
                {
                    if (isLeftUp)
                    {
                        isPreviousItem = true;
                        PreviousItem(null, null);
                        isLeftUp = false;
                    }
                }
            }
            else
            {
                isPreviousItem = false;
            }

            Joint updatedJoint = new Joint();
            updatedJoint.ID = jointLeft.ID;
            updatedJoint.TrackingState = JointTrackingState.Tracked;
            updatedJoint.Position = vector;

            //Canvas.SetLeft(ellipse, updatedJoint.Position.X);
            //Canvas.SetTop(ellipse, updatedJoint.Position.Y);
        }

        private float ScaleVector(int length, float position)
        {
            float value = (((((float)length) / 1f) / 2f) * position) + (length / 2);
            if (value > length)
            {
                return (float)length;
            }
            if (value < 0f)
            {
                return 0f;
            }
            return value;
        }

    }
}
